package com.wei.web.middleware.httpfilter.auth.security;

import com.wei.web.middleware.httpfilter.auth.security.filter.NothingAuthenticationFilter;
import com.wei.web.middleware.httpfilter.auth.security.model.SecurityConfigDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

@Slf4j
public class SecurityFilterChainFactoryBean extends AbstractFactoryBean<SecurityFilterChain> {

    private SecurityConfigDTO.AuthConfigDTO authConfig;

    private HttpSecurity http;

    public SecurityFilterChainFactoryBean() {
    }

    @Override
    public Class<?> getObjectType() {
        return SecurityFilterChain.class;
    }

    @Override
    protected SecurityFilterChain createInstance() throws Exception {
        DummyAuthenticationProvider dummyAuthenticationProvider = new DummyAuthenticationProvider();

        ProviderManager providerManager = new ProviderManager(new DummyAuthenticationProvider());
        providerManager.setEraseCredentialsAfterAuthentication(false);
        AuthenticationManager authenticationManager = providerManager;

        NothingAuthenticationFilter nothingAuthenticationFilter = new NothingAuthenticationFilter("/**", authenticationManager);


        http.csrf(AbstractHttpConfigurer::disable);
        http.headers(AbstractHttpConfigurer::disable);
        http.sessionManagement(sessionManagementCustomizer -> sessionManagementCustomizer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        http.cors(AbstractHttpConfigurer::disable);

        // Authentication and Authorization
        // filter add here must be annotated @Component, otherwise WebSecurity.ignoring() will not work. Refer to: https://github.com/spring-projects/spring-security/issues/3958
        http.authorizeRequests(a -> a.requestMatchers("/web/demo/ticket/**").authenticated()
                .anyRequest().denyAll());
        http.authenticationProvider(dummyAuthenticationProvider);
        http.addFilterAfter(nothingAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        // set
//        http.authenticationManager(authenticationManager());

        // access denied exception handler , this is FilterInvocation level, not method level.
        http.exceptionHandling(e -> e.accessDeniedHandler(
                (request, response, accessDeniedException) -> {
                    String errorMessage = "Forbidden: No permission for: " + request.getMethod() + " " + request.getRequestURI();
                    log.error(errorMessage);
                    response.setStatus(HttpStatus.FORBIDDEN.value());
                    response.getOutputStream().println(errorMessage);
                }
        ));

        // Require Explicit Saving of SecurityContextRepository
        http.securityContext((securityContext) -> securityContext
                .securityContextRepository(new HttpSessionSecurityContextRepository())
        );
        return http.build();
    }

    public SecurityConfigDTO.AuthConfigDTO getAuthConfig() {
        return authConfig;
    }

    public void setAuthConfig(SecurityConfigDTO.AuthConfigDTO authConfig) {
        this.authConfig = authConfig;
    }

    public HttpSecurity getHttp() {
        return http;
    }

    public void setHttp(HttpSecurity http) {
        this.http = http;
    }
}
